home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-11-19 | 49.0 KB | 1,180 lines |
- *-----------------------------------------------------------------------
- *-- Program...: FINANCE.PRG
- *-- Programmer: Ken Mayer (CIS: 71333,1030)
- *-- Date......: 07/27/1993
- *-- Notes.....: These finance functions are for use with interest rates
- *-- and such. See the file README.TXT for details about the
- *-- use of this library file.
- *--
- *-- NOTES ABOUT THESE ROUTINES (the ones written by Jay
- *-- Parsons) The functions that use (1+nRate)^nPeriods
- *-- require that the rate be stated in the same terms as the
- *-- compounding period. That is, for monthly compounding the
- *-- nRate should be the annual rate / 12, and the nPeriods
- *-- the number of months, and so forth.
- *--
- *-- If the situation involves continuous compounding, state
- *-- the rate as the exponent of the annual rate, less 1, and
- *-- state the periods in years. Accordingly, to find the
- *-- value in 30 months of a $1000 investment continuously
- *-- compounded at 6%, use:
- *-- FuturVal(1000,exp(.06)-1,30/12)
- *--
- *-- These functions (except NPV(), which sums a series of
- *-- equal or unequal cash flows), are designed for use with
- *-- a single "investment", one payment or receipt. If the
- *-- problem involves a series of equal payments or receipts
- *-- like a mortgage loan, a Holiday Club or an annuity, the
- *-- fv() and pv() functions built in to dBASE IV should be
- *-- used instead where possible.
- *-----------------------------------------------------------------------
-
- FUNCTION Discount
- *-----------------------------------------------------------------------
- *-- Programmer..: Jay Parsons (CIS: 72662,1302)
- *-- Date........: 03/01/1992
- *-- Notes.......: Compute the present value of an amount to be received
- *-- at the end of a number of periods given a periodic
- *-- interest rate.
- *-- Written for.: dBASE IV, 1.1
- *-- Rev. History: 03/01/1992 -- Original Release
- *-- Calls.......: None
- *-- Called by...: Any
- *-- Usage.......: Discount(<nFuturVal>,<nRate>,<nPeriods>)
- *-- Example.....: ?Discount(1000,.08,6)
- *-- Returns.....: Numeric
- *-- Parameters..: nFuturVal = the amount to be received/paid in the
- *-- future
- *-- nRate = the periodic rate of interest
- *-- nPeriods = the number of periods
- *-----------------------------------------------------------------------
-
- parameters nFuturVal, nRate, nPeriods
-
- RETURN m->nFuturVal / ( 1 + m->nRate ) ^ m->nPeriods
- *-- EoF: Discount()
-
- FUNCTION FuturVal
- *-----------------------------------------------------------------------
- *-- Programmer..: Jay Parsons (CIS: 72662,1302)
- *-- Date........: 03/01/1992
- *-- Notes.......: Compute the future value of an initial amount at
- *-- compound interest received at a given periodic rate
- *-- for a number of periods.
- *-- Written for.: dBASE IV, 1.0
- *-- Rev. History: 03/01/1992 -- Original Release
- *-- Calls.......: None
- *-- Called by...: Any
- *-- Usage.......: FuturVal(<nPresVal>,<nRate>,<nPeriods>)
- *-- Example.....: ?FuturVal(10000,.06,48)
- *-- Returns.....: Numeric
- *-- Parameters..: nPresVal = Present Value
- *-- nRate = Periodic interest rate
- *-- nPeriods = Number of periods to calculate for
- *-----------------------------------------------------------------------
-
- parameters nPresVal, nRate, nPeriods
-
- RETURN m->nPresVal * ( 1 + m->nRate ) ^ m->nPeriods
- *-- EoF: FuturVal()
-
- FUNCTION Rate
- *-----------------------------------------------------------------------
- *-- Programmer..: Jay Parsons (CIS: 72662,1302)
- *-- Date........: 03/01/1992
- *-- Notes.......: Compute rate of periodic interest needed to produce a
- *-- future value from a present value in a given number of
- *-- periods. If the periods are not years, you'll
- *-- probably want to multiply the rate returned by the
- *-- number of periods in a year to obtain the equivalent
- *-- annual rate.
- *-- Written for.: dBASE IV, 1.1
- *-- Rev. History: 03/01/1992 -- Original Release
- *-- Calls.......: None
- *-- Called by...: Any
- *-- Usage.......: Rate(<nFutVal>,<nPresVal>,<nPeriods>)
- *-- Example.....: ?Rate(50000,10000,48)
- *-- Returns.....: Numeric
- *-- Parameters..: nFutVal = Future Value
- *-- nPresVal = Present Value
- *-- nPeriods = Number of periods to calculate for
- *-----------------------------------------------------------------------
-
- parameters nFutVal, nPresVal, nPeriods
-
- RETURN ( nFutVal / m->nPresVal ) ^ ( 1 / m->nPeriods ) - 1
- *-- EoF: Rate()
-
- FUNCTION ContRate
- *-----------------------------------------------------------------------
- *-- Programmer..: Jay Parsons (CIS: 72662,1302)
- *-- Date........: 03/01/1992
- *-- Notes.......: Rate if compounding is continuous. Periods must be
- *-- years.
- *-- Written for.: dBASE IV, 1.1
- *-- Rev. History: 03/01/1992 -- Original Release
- *-- Calls.......: RATE() Function in FINANCE.PRG
- *-- Called by...: Any
- *-- Usage.......: ContRate(<nFutVal>,<nPresVal>,<nYears>)
- *-- Example.....: ?ContRate(50000,10000,4)
- *-- Returns.....: Numeric
- *-- Parameters..: nFutVal = Future Value
- *-- nPresVal = Present Value
- *-- nYears = Number of years to calculate for
- *-----------------------------------------------------------------------
-
- parameters nFutVal, nPresVal, nYears
-
- RETURN log( 1 + Rate( m->nFutVal, m->nPresVal, m->nYears ) )
- *-- EoF: ContRate()
-
- FUNCTION NPV
- *-----------------------------------------------------------------------
- *-- Programmer..: Tony Lima (CIS: 72331,3724) and
- *-- Jay Parsons (CIS: 72662,1302)
- *-- Date........: 03/01/1992
- *-- Notes.......: Net present value of array aCashflow[ nPeriods ]
- *-- Calculates npv given assumed rate and # periods.
- *-- See "Other inputs" below for instructions/details ...
- *-- Written for.: dBASE IV, 1.1
- *-- Rev. History: 03/01/1992 -- Original Release
- *-- Calls.......: None
- *-- Called by...: Any
- *-- Usage.......: NPV(<nRate>,<nPeriods>)
- *-- Example.....: ? NPV( .06, 6 )
- *-- Returns.....: Float = value of the project at given rate
- *-- Parameters..: nRate = Interest Rate
- *-- nPeriods = Number of Periods to calculate for
- *-- Other inputs: Requires the array aCashflow[ ] set up before calling.
- *-- Each of its elements [n] holds the cash flow at the
- *-- beginning of period n, with a negative amount
- *-- indicating a cash outflow. Elements of value 0 must
- *-- be included for all periods with no cash flow, and all
- *-- periods must be of equal length.
- *-- If the project is expected to require an immediate
- *-- outlay of $6,000 and to return $2,000 at the end of
- *-- each of the first five years thereafter, the array
- *-- will be:
- *-- aCashflow[1] = -6000
- *-- aCashflow[2] = 2000
- *-- aCashflow[3] = 2000
- *-- * * *
- *-- aCashflow[6] = 2000
- *--
- *-- If the cash flows are at the end of the periods,
- *-- rather than at the beginning, assign 0 to
- *-- aCashFlow[1], then assign cash flows successively.
- *-- aCashFlow[2] will then represent the cash flow at the
- *-- end of period 1, rather than at the beginning of
- *-- period 2, which is the same thing.
- *--
- *-- Rewriting the function to have array name passed as
- *-- a parameter is possible, but will slow down execution
- *-- to an extent that will be very noticeable if this
- *-- function is being repeatedly executed, as by Zeroin()
- *-- to find an Internal Rate of Return.
- *-----------------------------------------------------------------------
-
- parameters nRate, nPeriods
- private nDiscount, nFactor, nPeriod, nNpv
- m->nPeriod = 1
- m->nNPV = aCashflow[ 1 ]
- m->nDiscount = float( 1 )
- m->nFactor = 1 / ( 1 + m->nRate )
- do while m->nPeriod < m->nPeriods
- m->nPeriod = m->nPeriod + 1
- m->nDiscount = m->nDiscount * m->nFactor
- m->nNPV = m->nNPV + aCashflow[ m->nPeriod ] * m->nDiscount
- enddo
-
- RETURN m->nNPV
- *-- EoF: NPV()
-
- FUNCTION Irr
- *-----------------------------------------------------------------------
- *-- Programmer..: Jay Parsons (CIS: 72662,1302)
- *-- Based on code by Tony Lima (CIS: 72331,3724), 1990.
- *-- Date........: 4/13/1992
- *-- Notes.......: Finds internal rate of return using Zeroin().
- *-- An internal rate of return is an interest rate at
- *-- which the net present value of a series of cash flows
- *-- is zero. In the normal case of an investment, where
- *-- cash flows out at first, then comes back in later
- *-- periods, the IRR gives the interest rate for an
- *-- equally-good deal, and investments with higher IRR
- *-- should be considered first.
- *--
- *-- As this function uses the Npv() function to evaluate
- *-- the cash flows at each assumed rate, and Npv()
- *-- requires for speed that the cash flows be placed in
- *-- the array aCashflow[], the cash flows must be placed
- *-- there before calling this function. The number of
- *-- rows in aCashflow[] is a parameter passed through
- *-- by Zeroin() to Npv().
- *--
- *-- Written for.: dBASE IV Version 1.5
- *-- Rev. History: Original function 1990.
- *-- Modified to match Zeroin(), Npv(), 4/13/1992
- *-- Calls.......: Zeroin() Function in STATS.PRG
- *-- Arrayrows() Function in ARRAYS.PRG
- *-- Called by...: Any
- *-- Usage.......: ? Irr( <fX1>, <fX2>, n_Flag )
- *-- Example.....: nRate = Irr( 11, 0, 200, n_Flag )
- *-- Returns : a float value representing Irr, if n_Flag < 3.
- *-- Parameters..: fX1 = lowest plausible rate of return from this
- *-- project.
- *-- fX2 = highest plausible rate of return, ditto.
- *-- n_Flag = an integer to signal success ( < 3 ) or
- *-- failure.
- *-- Other input.: Requires advance setup of array to be called by Npv,
- *-- as furnished "aCashflow[]", to hold cash flows.
- *-- Side effects: Uses and alters a global numeric variable, here
- *-- called "n_Flag", to report error conditions resulting
- *-- in value returned being meaningless.
- *-----------------------------------------------------------------------
-
- parameters fX1, fX2, n_Flag
-
- RETURN Zeroin( "Npv", m->fX1, m->fX2, float( 1 / 10 ^ 6 ), 100, ;
- m->n_Flag, arrayrows( "aCashflow" ) )
- *-- EoF: Irr()
-
- FUNCTION Irr2
- *----------------------------------------------------------------------
- *-- Programmer..: Ron Allen (CIS: 71201,2502)
- *-- Date........: 01/25/1993
- *-- Notes.......: Returns internal rate of return on an investment from
- *-- evenly-spaced periodic cashflows. The UDF
- *-- simultaneously accumulates the periodic Net Present
- *-- Values of the individual cashflows along with the
- *-- first derivative of the function. After the summation
- *-- is completed for each guess, the guess is adjusted by
- *-- subtracting the ratio of the function to its
- *-- derivative.
- *-- Written for.: dBASE IV, 1.5
- *-- Rev. History: 01/25/1993 -- Original
- *-- 01/28/1993 - 1.01 -- to add missing private variables.
- *-- To count iterations without sign change in PV. Move
- *-- division by nRatio outside inner loop.
- *-- Calls.......: None
- *-- Called by...: Any
- *-- Usage.......: Irr2(<nN>, <cFlow>, <lSw>, <nGuess>)
- *-- Example.....: Rate = Irr2(6, "Cash", Switch, .01)
- *-- Returns.....: Internal Rate of Return.
- *-- Parameters..: nN = number of cashflows in model
- *-- cFlow = name of the array holding the cashflows
- *-- lSw = name of a logical variable to be switched to
- *-- indicate valid IRR returned (.t.).
- *-- nGuess = optional guess for initialing search.
- *-----------------------------------------------------------------------
-
- parameters nN, cFlow, lSw, nGuess
- private nI, nPosVal, nNegVal, nCurVal, nIRR, nNuDelta, nOlDelta, ;
- nBigchange
- private nSignChng, nDiscount, nRatio, nSumPV, nCurrPV, nSumDeriv, ;
- nOldPV
- private nIters, lSw1
- store 0 to m->nI, m->nPosVal, m->nNegVal, m->nIters
- store .t. to m->lSw
- store .f. to m->lSw1
- declare nCashFlow[m->nN]
-
- *-- Transfer cashflows to a private array and separate negatives from
- *-- positives
- do while m->nI < m->nN
- m->nI = m->nI+1
- store &cFlow.[m->nI] to nCashFlow[m->nI], m->nCurVal
- if m->nCurVal < 0
- m->nNegVal = m->nNegVal + m->nCurVal
- else
- m->nPosVal = m->nPosVal + m->nCurVal
- endif
- enddo
- if m->nNegVal = 0 .or. m->nPosVal = 0
- wait "Must have at least one positive and one negative value"
- endif
-
- *-- Use initializing guess if provided, otherwise calculate from
- *-- weighted average returns.
-
- if pcount() = 4
- m->nIRR = m->nGuess
- else
- m->nIRR = ((-m->nPosVal/m->nNegVal)-1)/m->nN
- endif
-
- *-- Housekeeping summary accumulators, etc., before entering loop
- store 1 to m->nNuDelta, m->nOlDelta
- store 0 to m->nSignChng, m->nBigChange
-
- *-- Loop until estimated rate indicated accuracy
- do while abs(m->nNuDelta) > .000001
- store 0 to m->nI, m->nSumPV, m->nSumDeriv
-
- *-- Set up cumulative denominator to calculate
- *-- incremental NPV
- m->nDiscount = 1
- m->nRatio = 1 + m->nIRR
- do while m->nI < m->nN
- m->nI = m->nI+1
- m->nDiscount = m->nDiscount/m->nRatio
-
- *-- Calculate incremental PV and add to sum
- m->nCurrPV = m->nDiscount * nCashFlow[m->nI]
- m->nSumPV = m->nSumPV + m->nCurrPV
-
- *-- Add incremental first derivative to derivative
- *-- sum
- m->nSumDeriv = m->nSumDeriv - m->nI * m->nCurrPV
- enddo
-
- *-- count iterations and test for sign change of future value
- if .not. m->lSw1 .and. m->nIters > 0
- m->lSw1 = iif(sign(nOldPV) = sign(m->nSumPV),.f.,.t.)
- endif
- m->nIters = m->nIters + 1
- m->nOldPV = m->nSumPV
-
-
- *-- Calculate indicated change in IRR
- m->nNuDelta = m->nRatio * m->nSumPV/m->nSumDeriv
-
- *-- Test for big changes in adjusted IRR, limit to 10 times
- *-- current guess for IRR and count big changes.
- if abs(m->nNuDelta/m->nIRR) > 10
- m->nNuDelta = sign(m->nNuDelta) * 10 * m->nIRR
- m->nBigChange = m->nBigChange + 1
- endif
- m->nIRR = m->nIRR - m->nNuDelta && Make adjustment to guess
- && for IRR
-
- *-- Count reversals in adjustments to limit hunting
- m->nSignChng = m->nSignChng + iif(sign(m->nNuDelta) + ;
- sign(m->nOlDelta) = 0,1,0)
- m->nOlDelta = m->nNuDelta
-
- *-- Test for hunting, too many bigchanges or too large a
- *-- solution and set external switch if abnormal exit is
- *-- used.
- if m->nSignChng + m->nBigChange > 10 .or. abs(m->nIRR) > 100 ;
- .or. (m->nIters > 9 .and. .not. m->lSw1)
- store .f. to m->lSw
- exit
- endif
- enddo
-
- RETURN m->nIRR
- *-- EoF: Irr2()
-
- FUNCTION Mirr
- *-----------------------------------------------------------------------
- *-- Programmer..: Ron Allen (CIS: 71201,2502)
- *-- Date........: 01/27/1993
- *-- Notes.......: Used to calculate the Modified Internal Rate of Return
- *-- for evenly-spaced periodic cashflows. The
- *-- modifications assume that more realistic investment
- *-- models should account for the cost of borrowing or the
- *-- lower 'safe' rate for keeping reserve funds to cover
- *-- outlays and the fact that reinvestments will be made
- *-- at some other rate than the IRR itself. This model
- *-- calculates the answer directly, therefore more rapidly
- *-- than the iterative approach used by IRR.
- *-- Written for.: dBASE IV, 1.5
- *-- Rev. History: 01/27/1993 -- Original Release
- *-- Calls.......: None
- *-- Called by...: Any
- *-- Usage.......: Mirr(<nN>, <cFlow>, <nRrate>, <nFrate>)
- *-- Example.....: Rate = Mirr(6, "Cash", .1, .14)
- *-- Returns.....: Modified Internal Rate of Return per period.
- *-- Parameters..: nN = number of cashflows in model
- *-- cFlow = name of the array holding the cashflows
- *-- nRrate = Reinvestment rate for positive cashflows.
- *-- nFrate = 'Safe' rate expected on reserve funds to
- *-- cover disbursements.
- *-----------------------------------------------------------------------
-
- parameters nN, cFlow, nRrate, nFrate
- private nI, nNegVal, nPosVal, nCurVal
- store 0 to m->nI, m->nNegVal, m->nPosVal
-
- *-- Pass through array once computing present value of negative
- *-- cashflows at 'safe' rate and present value of positive values
- *-- at the reinvestment rate.
- do while m->nI < m->nN
- m->nI = m->nI+1
- m->nCurVal = &cFlow[m->nI].
- m->nCurVal = m->nCurVal*(1+iif(m->nCurVal<0,nFrate,nRrate))^;
- -(m->nI-1)
- if m->nCurVal < 0
- m->nNegVal = m->nNegVal + m->nCurVal
- else
- m->nPosVal = m->nPosVal + m->nCurVal
- endif
- enddo
- if abs(m->nNegVal) = 0 .or. m->nPosVal = 0
- wait "There must be at least one negative and one positive value!"
- RETURN 0
- endif
-
- *-- Calculate the rate of return required to yield a future value
- *-- of the positive values reinvested at nRrate from the present
- *-- value of the negative values invested at the 'safe' rate.
-
- RETURN ((-m->nPosVal * (1+m->nRrate)^(m->nN-1))/m->nNegVal)^;
- (1/(m->nN-1))-1
- *-- EoF: Mirr()
-
- FUNCTION Xmirr
- *-----------------------------------------------------------------------
- *-- Programmer..: Ron Allen (CIS: 71201,2502)
- *-- Date........: 01/27/1993
- *-- Notes.......: Used to calculate the Modified Internal Rate of Return
- *-- from cashflows on random dates. Except for the need to
- *-- supply both the dates of transactions and the
- *-- cashflows in an 'nN' by 2 array, the other inputs are
- *-- the same as in Mirr(). Dates may be in random order
- *-- except for the first date. The first date in the array
- *-- establishes the date to which present value applies.
- *-- Enter 'Safe' rate for reserves and 'Reinvestment' rate
- *-- for positive cashflows as annual rates, e.g., .075 for
- *-- 7.5%.
- *-- Written for.: dBASE IV, 1.5
- *-- Rev. History: 01/27/1993 -- 1.01 - to allow entry of 'Safe' reserve
- *-- rate and 'Reinvestment' rate as annual rates rather
- *-- than rates. Also, to return the 'effective' rate of
- *-- interest when compounded daily, rather than the
- *-- 'nominal' rate.
- *-- Calls.......: None
- *-- Called by...: Any
- *-- Usage.......: Xmirr(<nN>, <cFlow>, <nRrate>, <nFrate>)
- *-- Example.....: Rate = Xmirr(5, "Cash", .14, .1)
- *-- Returns.....: Annualized Effective Modified Internal Rate of Return
- *-- based on daily compounded interest.
- *-- Parameters..: nN = number of cashflows in model
- *-- cFlow = name of 'nN' by 2 array holding the dates
- *-- (col 1) and cashflow amounts (col 2).
- *-- nRrate = Reinvestment rate for positive cashflows.
- *-- nFrate = 'Safe' rate expected on reserve funds to
- *-- cover disbursements.
- *-----------------------------------------------------------------------
-
- parameters nN, cFlow, nRrate, nFrate
- private nI, nCurVal, nNegVal, nPosVal, dPDate
- private dMaxDate, dCurDate, nCurN, nMirr
-
- store 0 to m->nI, m->nNegVal, m->nPosVal
- store (1+m->nRrate)^(1/365)-1 to m->nRrate
- store (1+m->nFrate)^(1/365)-1 to m->nFrate
- store &cFlow.[1,1] to m->dPDate
- m->dMaxDate = m->dPDate
-
- do while m->nI < m->nN
- m->nI = m->nI+1
- m->nCurVal = &cFlow.[m->nI,2]
- m->dCurDate = &cFlow.[m->nI,1]
- m->dMaxDate = max(m->dCurDate,m->dMaxDate)
- m->nCurN = m->dCurDate-m->dPDate
- m->nCurVal = m->nCurVal/(1+iif(m->nCurVal<0,m->nFrate,;
- m->nRrate))^m->nCurN
- if m->nCurVal < 0
- m->nNegVal = m->nNegVal + m->nCurVal
- else
- m->nPosVal = m->nPosVal + m->nCurVal
- endif
- enddo
- if m->nNegVal = 0 .or. m->nPosVal = 0
- wait;
- " There must be at least one negative and one positive value! "
- RETURN 0
- endif
- m->nN = m->dMaxDate - m->dPDate
- m->nMirr = ((-m->nPosVal * (1+m->nRrate)^(m->nN-1))/m->nNegVal)^;
- (1/(m->nN-1))-1
-
- RETURN (1+m->nMirr)^365-1
- *-- EoF: Xmirr()
-
- FUNCTION Xirr
- *-----------------------------------------------------------------------
- *-- Programmer..: Ron Allen (CIS: 71201,2502)
- *-- Date........: 01/25/1993
- *-- Notes.......: Used to calculate the Internal Rate of Return from
- *-- cashflows on random dates. Except for the need to
- *-- supply both the dates of transactions and the
- *-- cashflows in an 'nN' by 2 array, the other inputs are
- *-- the same as in Irr(). Dates may be in random order
- *-- except for the first date. The first date in the array
- *-- establishes the date to which present value applies.
- *-- Written for.: dBASE IV, 1.5
- *-- Rev. History: 01/25/1993 -- Original
- *-- 01/28/1993 - 1.01 -- to return 'effective' rate of
- *-- interest when compounded daily rather than the
- *-- 'nominal' rate. Also to count iterations without a
- *-- sign change in PV. Move division by nRatio outside
- *-- inner loop.
- *-- Calls.......: None
- *-- Called by...: Any
- *-- Usage.......: Irr(<nN>, <cFlow>, <lSw>, <nGuess>)
- *-- Example.....: Rate = Irr(5, "Cash", "Switch", .01)
- *-- Returns.....: Effective Internal Rate of Return.
- *-- Parameters..: nN = number of cashflows in model
- *-- cFlow = name of the 'nN' by 2 array holding the
- *-- dates (col 1) and cashflows (col 2). Dates
- *-- may be entered in any order except for the
- *-- date, which is the date to which present
- *-- value applies.
- *-- lSw = name of a logical variable to be switched to
- *-- indicate valid IRR returned (.t.).
- *-- nGuess = optional guess for initializing search.
- *-----------------------------------------------------------------------
-
- parameters nN, cFlow, lSw, nGuess
- private nI, nPosVal, nNegVal, nCurVal, nIRR, nNuDelta, nOlDelta, ;
- nBigchange
- private nSignChng, nRatio, dPDate, dMaxDate, nCurrPV, nSumDeriv
- private nSumPV, dCurDate, nIters, lSw1
-
- store 0 to m->nI, m->nPosVal, m->nNegVal, m->nIters
- store .t. to m->lSw
- declare nCashFlow[m->nN,2]
- store &cFlow.[1,1] to m->dMaxDate, m->dPDate
- store .f. to m->lSw1
-
- *-- Transfer cashflows to a private array and separate negatives from
- *-- positives. Find last date.
- do while m->nI < m->nN
- m->nI = m->nI+1
- store &cFlow.[m->nI,1] to nCashFlow[m->nI,1], m->dCurDate
- store &cFlow.[m->nI,2] to nCashFlow[m->nI,2], m->nCurVal
- store max(m->dCurDate,m->dMaxDate) to m->dMaxDate
- if m->nCurVal < 0
- m->nNegVal = m->nNegVal + m->nCurVal
- else
- m->nPosVal = m->nPosVal + m->nCurVal
- endif
- enddo
- if m->nNegVal = 0 .or. m->nPosVal = 0
- wait "Must have at least one positive and one negative value"
- endif
-
- *-- Use initializing guess if provided, otherwise calculate from
- *-- weighted average returns.
- if pcount() = 4
- m->nIRR = m->nGuess
- else
- m->nIRR = (((m->nPosVal+m->nNegVal-ncashflow[1,2])/-;
- nCashFlow[1,2])-1)/(m->dMaxDate-m->dPDate)
- endif
-
- *-- Housekeeping summary accumulators, etc., before entering loop
- store 1 to m->nNuDelta, m->nOlDelta
- store 0 to m->nSignChng, m->nBigChange
-
- *-- Loop until estimated rate indicated accuracy
- do while abs(m->nNuDelta) > .000001
- store 0 to m->nI, m->nSumPV, m->nSumDeriv
- store 1 + m->nIrr to m->nRatio
- do while m->nI < m->nN
- m->nI = m->nI+1
-
- *-- Calculate incremental PV and add to sum
- m->nCurrPV = nCashFlow[m->nI,2] / m->nRatio^;
- (nCashFlow[m->nI,1] - m->dPDate)
- m->nSumPV = m->nSumPV + m->nCurrPV
-
- *-- Add incremental first derivative to derivative
- *-- sum
- m->nSumDeriv = m->nSumDeriv - (nCashFlow[m->nI,1] -;
- m->dPDate) * m->nCurrPV
- enddo
-
- *-- count iterations and test for sign change of future value
- if .not. m->lSw1 .and. m->nIters > 0
- m->lSw1 = iif(sign(m->nOldPV) = sign(m->nSumPV),.f.,.t.)
- endif
- m->nIters = m->nIters + 1
- m->nOldPV = m->nSumPV
-
- *-- Calculate indicated change in IRR
- m->nNuDelta = m->nRatio * m->nSumPV/m->nSumDeriv
-
- *-- Test for big changes in adjusted IRR, limit to 10 times
- *-- current guess for IRR and count big changes.
- if abs(m->nNuDelta/m->nIRR) > 10
- m->nNuDelta = sign(m->nNuDelta) * 10 * m->nIRR
- m->nBigChange = m->nBigChange + 1
- endif
- m->nIRR = m->nIRR - m->nNuDelta
- && Make adjustment to guess for IRR
-
- *-- Count reversals in adjustments to limit hunting
- m->nSignChng = m->nSignChng + iif(sign(m->nNuDelta) + ;
- sign(m->nOlDelta) = 0,1,0)
- m->nOlDelta = m->nNuDelta
-
- *-- Test for hunting, too many bigchanges or too large a
- *-- solution and set external switch if abnormal exit is
- *-- used.
- if m->nSignChng + m->nBigChange > 10 .or. abs(m->nIRR) > 100;
- .or. (m->nIters > 9 .and. .not. m->lSw1)
- store .f. to m->lSw
- exit
- endif
- enddo
-
- RETURN (1+m->nIrr)^365 -1
- *-- EoF: Xirr()
-
- FUNCTION FVirr
- *-----------------------------------------------------------------------
- *-- Programmer..: Ron Allen (CIS: 71201,2502)
- *-- Date........: 01/28/1993
- *-- Notes.......: Returns same roots as Irr(), but averages 20% faster.
- *-- Irr() searches for the roots of NPV (Net Present
- *-- Value), while FVirr() searches for the same roots of
- *-- NFV (Net Future Value), both with respect to the rate
- *-- of return. The user may wish to use this UDF in place
- *-- of Irr() and use Irr() as an alternate to help locate
- *-- more multiple solutions. The reason this UDF is
- *-- 'usually' faster is due to the fact that the NFV curve
- *-- is 'usually' steeper as it crosses the zero axis.
- *-- Written for.: dBASE IV, 1.5
- *-- Rev. History: 01/28/1993 -- Original
- *-- 01/28/1993 -- 1.01 - Modified Irr() to use Net Future
- *-- Value curve instead of Net Present Value curve.
- *-- Calls.......: None
- *-- Called by...: Any
- *-- Usage.......: Irr(<nN>, <cFlow>, <lSw>, <nGuess>)
- *-- Example.....: Rate = Irr(6, "Cash", Switch, .01)
- *-- Returns.....: Internal Rate of Return.
- *-- Parameters..: nN = number of cashflows in model
- *-- cFlow = name of the array holding the cashflows
- *-- lSw = name of a logical variable to be switched to
- *-- indicate valid IRR returned (.t.).
- *-- nGuess = optional guess for initialing search.
- *-----------------------------------------------------------------------
-
- parameters nN, cFlow, lSw, nGuess
- private nI, nPosVal, nNegVal, nCurVal, nIRR, nNuDelta, nOlDelta, ;
- nBigchange
- private nSignChng, nDiscount, nRatio, nSumFV, nCurrFV, nSumDeriv, ;
- nOldFV
- private nIters, lSw1
-
- store 0 to m->nI, m->nPosVal, m->nNegVal, m->nIters
- store .t. to m->lSw
- store .f. to m->lSw1
- declare nCashFlow[m->nN]
-
- *-- Transfer cashflows to a private array and separate negatives from
- *-- positives
- do while m->nI < m->nN
- m->nI = m->nI+1
- store &cFlow.[m->nI] to nCashFlow[m->nI], m->nCurVal
- if m->nCurVal < 0
- m->nNegVal = m->nNegVal + m->nCurVal
- else
- m->nPosVal = m->nPosVal + m->nCurVal
- endif
- enddo
- if m->nNegVal = 0 .or. m->nPosVal = 0
- wait "Must have at least one positive and one negative value"
- endif
-
- *-- Use initializing guess if provided, otherwise calculate from
- *-- weighted average returns.
- if pcount() = 4
- m->nIRR = m->nGuess
- else
- m->nIRR = ((-m->nPosVal/m->nNegVal)-1)/m->nN
- endif
-
- *-- Housekeeping summary accumulators, etc., before entering loop
- store 1 to m->nNuDelta, m->nOlDelta
- store 0 to m->nSignChng, m->nBigChange
-
- *-- Loop until estimated rate indicated accuracy
- do while abs(m->nNuDelta) > .000001
- store 0 to m->nI, nSumFV, m->nSumDeriv
-
- *-- Set up cumulative denominator to calculate
- *-- incremental NFV
- m->nRatio = 1 + m->nIRR
- m->nDiscount = m->nRatio^m->nN
- do while m->nI < m->nN
- m->nI = m->nI+1
- m->nDiscount = m->nDiscount/m->nRatio
-
- *-- Calculate incremental FV and add to sum
- m->nCurrFV = m->nDiscount * nCashFlow[m->nI]
- m->nSumFV = m->nSumFV + m->nCurrFV
-
- *-- Add incremental first derivative to derivative
- *-- sum
- m->nSumDeriv = m->nSumDeriv - m->nI * m->nCurrFV
- enddo
-
- *-- count iterations and test for sign change of future value
- if .not. m->lSw1 .and. m->nIters > 0
- m->lSw1 = iif(sign(m->nOldFV) = sign(m->nSumFV),.f.,.t.)
- endif
- m->nIters = m->nIters + 1
- m->nOldFV = m->nSumFV
-
- *-- Calculate indicated change in IRR
- m->nNuDelta = m->nRatio * m->nSumFV/m->nSumDeriv
-
- *-- Test for big changes in adjusted IRR, limit to 10 times
- *-- current guess for IRR and count big changes.
- if abs(m->nNuDelta/m->nIRR) > 10
- m->nNuDelta = sign(m->nNuDelta) * 10 * m->nIRR
- m->nBigChange = m->nBigChange + 1
- endif
- m->nIRR = m->nIRR - m->nNuDelta
- && Make adjustment to guess for IRR
-
- *-- Count reversals in adjustments to limit hunting
- m->nSignChng = m->nSignChng + iif(sign(m->nNuDelta) +;
- sign(m->nOlDelta) = 0,1,0)
- m->nOlDelta = m->nNuDelta
-
- *-- Test for hunting, too many bigchanges or too large a
- *-- solution and set external switch if abnormal exit is
- *-- used.
- if m->nSignChng + m->nBigChange > 10 .or. abs(m->nIRR) >;
- 100 .or. (m->nIters > 9 .and. .not. m->lSw1)
- store .f. to m->lSw
- exit
- endif
- enddo
-
- RETURN m->nIRR
- *-- EoF: FVirr()
-
- FUNCTION FVxirr
- *-----------------------------------------------------------------------
- *-- Programmer..: Ron Allen (CIS: 71201,2502)
- *-- Date........: 01/28/1993
- *-- Notes.......: Same as Xirr() except that the Net Future Value (NFV)
- *-- function is used instead of the Net Present Value
- *-- (NPV) function. The roots are the same, but this
- *-- function is usually faster for the same reasons that
- *-- FVirr() is faster than Irr(). As in Xirr(), all dates
- *-- except the first date in the array may be in random
- *-- order. The first date in the nN by 2 array along with
- *-- the maximum date establishes the range of the
- *-- investment analysis.
- *-- Written for.: dBASE IV, 1.5
- *-- Rev. History: 01/28/1993
- *-- 01/28/1993 -- 1.01 - Modified Xirr() to find roots of
- *-- the Net Future Value curve.
- *-- Calls.......: None
- *-- Called by...: Any
- *-- Usage.......: Irr(<nN>, <cFlow>, <lSw>, <nGuess>)
- *-- Example.....: Rate = Irr(5, "Cash", Switch, .01)
- *-- Returns.....: Effective Internal Rate of Return.
- *-- Parameters..: nN = number of cashflows in model
- *-- cFlow = name of the 'nN' by 2 array holding the
- *-- dates (col 1) and cashflows (col 2). Dates
- *-- may be entered in any order except for the
- *-- date, which is the date to which present
- *-- value applies.
- *-- lSw = name of a logical variable to be switched to
- *-- indicate valid IRR returned (.t.).
- *-- nGuess = optional guess for initializing search.
- *-----------------------------------------------------------------------
-
- parameters nN, cFlow, lSw, nGuess
- private nI, nPosVal, nNegVal, nCurVal, nIRR, nNuDelta, nOlDelta, ;
- nBigchange
- private nSignChng, nRatio, dPDate, dMaxDate, nCurrFV, nSumDeriv
- private nSumFV, dCurDate, lSw1, nIters
-
- store 0 to m->nI, m->nPosVal, m->nNegVal, m->nIters
- store .t. to m->lSw
- declare nCashFlow[m->nN,2]
- store &cFlow.[1,1] to m->dMaxDate, m->dPDate
-
- *-- Transfer cashflows to a private array and separate negatives from
- *-- positives. Find last date.
-
- do while m->nI < m->nN
- m->nI = m->nI+1
- store &cFlow.[m->nI,1] to nCashFlow[m->nI,1], m->dCurDate
- store &cFlow.[m->nI,2] to nCashFlow[m->nI,2], m->nCurVal
- store max(m->dCurDate,m->dMaxDate) to m->dMaxDate
- if m->nCurVal < 0
- m->nNegVal = m->nNegVal + m->nCurVal
- else
- m->nPosVal = m->nPosVal + m->nCurVal
- endif
- enddo
- if m->nNegVal = 0 .or. m->nPosVal = 0
- wait "Must have at least one positive and one negative value"
- endif
-
- *-- Use initializing guess if provided, otherwise calculate from
- *-- weighted average returns.
- if pcount() = 4
- m->nIRR = m->nGuess
- else
- m->nIRR = (((m->nPosVal+m->nNegVal-ncashflow[1,2])/-;
- nCashFlow[1,2])-1)/(m->dMaxDate-m->dPDate)
- endif
-
- *-- Housekeeping summary accumulators, etc., before entering loop
- store 1 to m->nNuDelta, m->nOlDelta
- store 0 to m->nSignChng, m->nBigChange
- store .f. to m->lSw1
-
- *-- Loop until estimated rate indicated accuracy
- do while abs(m->nNuDelta) > .000001
- store 0 to m->nI, m->nSumFV, m->nSumDeriv
- store 1 + m->nIrr to m->nRatio
- do while m->nI < m->nN
- m->nI = m->nI+1
-
- *-- Calculate incremental FV and add to sum
- m->nCurrFV = nCashFlow[m->nI,2] * m->nRatio^;
- (m->dMaxDate - nCashFlow[m->nI,1])
- m->nSumFV = m->nSumFV + m->nCurrFV
-
- *-- Add incremental first derivative to derivative
- *-- sum
- m->nSumDeriv = m->nSumDeriv + (m->dMaxDate - ;
- nCashFlow[m->nI,1]) * m->nCurrFV
- enddo
-
- *-- count iterations and test for sign change of future value
- if .not. m->lSw1 .and. m->nIters > 0
- m->lSw1 = iif(sign(m->nOldFV) = sign(m->nSumFV),.f.,.t.)
- endif
- m->nIters = m->nIters + 1
- m->nOldFV = m->nSumFV
-
- *-- Calculate indicated change in IRR
- m->nNuDelta = m->nRatio * m->nSumFV/m->nSumDeriv
-
- *-- Test for big changes in adjusted IRR, limit to 10 times
- *-- current guess for IRR and count big changes.
- if abs(m->nNuDelta/m->nIRR) > 10
- m->nNuDelta = sign(m->nNuDelta) * 10 * m->nIRR
- m->nBigChange = m->nBigChange + 1
- endif
- m->nIRR = m->nIRR - m->nNuDelta
- && Make adjustment to guess for IRR
-
- *-- Count reversals in adjustments to limit hunting
- m->nSignChng = m->nSignChng + iif(sign(m->nNuDelta) + ;
- sign(m->nOlDelta) = 0,1,0)
- m->nOlDelta = m->nNuDelta
-
- *-- Test for hunting, too many bigchanges or too large a
- *-- solution and set external switch if abnormal exit is
- *-- used.
- if m->nSignChng + m->nBigChange > 10 .or. abs(m->nIRR) > 100;
- .or. (m->nIters > 9 .and. .not. m->lSw1)
- store .f. to m->lSw
- exit
- endif
- enddo
-
- RETURN (1+m->nIrr)^365 -1
- *-- EoF: FVxirr()
-
- *-----------------------------------------------------------------------
- *-- Note: The following functions are here as a courtesy, as they are
- *-- used in at least one of the functions above.
- *-----------------------------------------------------------------------
-
- FUNCTION Zeroin
- *-----------------------------------------------------------------------
- *-- Programmer..: Tony Lima (CIS: 72331,3724) and
- *-- Jay Parsons (CIS: 72662,1302)
- *-- Date........: 04/13/1992
- *-- Notes.......: Finds a zero of a continuous function.
- *-- In substance, what this function does is close in on a
- *-- solution to a function that cannot otherwise be
- *-- solved. Assuming Y = f(X), if Y1 and Y2, the values of
- *-- the function for X1 and X2, have different signs,
- *-- there must be at least one value of X between X1 and
- *-- X2 for which Y = 0, if the function is continuous.
- *-- This function closes in on such a value of X by a
- *-- trial-and-error process.
- *--
- *-- This function is very slow, so a maximum number of
- *-- iterations is passed as a parameter. If the number
- *-- of iterations is exceeded, the function will fail to
- *-- find a root. If this occurs, pick different original
- *-- "X" values, increase the number of iterations or
- *-- increase the errors allowed. Once an approximate root
- *-- is found, you can use values of X close on either side
- *-- and reduce the error allowed to find an improved
- *-- solution. Also, of course, the signs of Y must be
- *-- different for the starting X values for the function
- *-- to proceed at all.
- *--
- *-- NOTE ESPECIALLY - There is NO guarantee that a root
- *-- returned by this function is the only one, or the
- *-- most meaningful. It depends on the function that this
- *-- function calls, but if that function has several
- *-- roots, any of them may be returned. This can easily
- *-- happen with such called functions as net present value
- *-- where the cash flows alternate from positive
- *-- to negative and back, and in many other "real life"
- *-- cases. See the discussion of @IRR in the documentation
- *-- of a good spreadsheet program such as Quattro Pro for
- *-- further information.
- *--
- *-- The method used by this function is a "secant and
- *-- bisect" search. The "secant" is the line connecting
- *-- two X,Y points on a graph using standard Cartesian
- *-- coordinates. Where the secant crosses the X axis is
- *-- the best guess for the value of X that will have
- *-- Y = 0, and will be correct if the function is linear
- *-- between the two points. The basic strategy is to
- *-- calculate Y at that value of X, then keep the new X
- *-- and that one of the old X values that had a Y-value
- *-- of opposite sign, and reiterate to close in.
- *--
- *-- If the function is a simple curve with most of the
- *-- change in Y close to one of the X-values, as often
- *-- occurs if the initial values of X are poorly chosen,
- *-- repeated secants will do little to find a Y-value
- *-- close to zero and will reduce the difference in
- *-- X-values only slightly. In this case the function
- *-- shifts to choosing the new X halfway between the
- *-- old ones, bisecting the difference and always
- *-- reducing the bracket by half, for a while.
- *--
- *-- While this function finds a "zero", it may be used
- *-- to find an X corresponding to any other value of Y.
- *-- Suppose the function of X is FUNCTION Blackbox( X )
- *-- and it is desired to find a value of X for which
- *-- f(X) = 7. The trick is to interpose a function
- *-- between Zeroin() and Blackbox() that will return a
- *-- 0 to Zeroin() whenever Blackbox() returns 7. By
- *-- calling that function, Zeroin() finds a value of
- *-- X for which Blackbox( X ) = 7, as required:
- *-- Result = Zeroin( "Temp", <other parameters omitted>)
- *--
- *-- FUNCTION Temp
- *-- parameters nQ
- *-- RETURN Blackbox( nQ ) - 7
- *--
- *-- Written for.: dBASE IV, 1.5
- *-- Rev. History: Original function 1990.
- *-- Modified to take optional parameters, 4/13/1992
- *-- Calls.......: The function whose name is first parameter.
- *-- Called by...: Any
- *-- Usage.......: Zeroin( <cFunction>, <m->fX1>, <fX2>, <fAbserror>, ;
- *-- <nMaxiter>, <n_Flag> ;
- *-- [, xPass1 [, xPass2 [, xPass3 ] ] ] )
- *-- Example.....: ? Zeroin( "Npv", 0, 200, .000001, 200, n_Flag, 11 )
- *-- Returns.....: a float value representing a root, if n_Flag < 3.
- *-- Parameters..: cFunction = the name of the function to solve for a
- *-- root.
- *-- fX1 = one of the X-values between which the root
- *-- is sought.
- *-- fX2 = the second of these values.
- *-- Note: These MUST be chosen so the f( X ) values for
- *-- the two of them have opposite signs (they must
- *-- bracket the result).
- *-- fAbserror = the absolute error allowed in the result.
- *-- nMaxiter = the maximum number of times to iterate.
- *-- n_Flag = an integer to signal success ( < 3 ) or
- *-- failure.
- *-- xPass1 . . . 3 = arguments to be passed through to
- *-- cFunction.
- *-- The parameter "n_Flag" should be passed as a variable
- *-- so it may be accessed on return. The limit of 9
- *-- literal parameters may require passing others as
- *-- variables. The "xPass" parameters are optional and
- *-- the fact there are three of them is arbitrary; they
- *-- exist to hold whatever parameters may be needed by
- *-- the function cFunction being called aside from
- *-- the value of X for which it is being evaluated.
- *-- Add more and change the 3 "&cFunc." lines below if
- *-- you need more.
- *-- Side effects: Uses and alters a global numeric variable, here called
- *-- "n_Flag", to report error conditions resulting in
- *-- value returned being meaningless. Possible n_Flag
- *-- values are:
- *-- 1 success - root found within error
- *-- allowed
- *-- 2 success - root was found exactly
- *-- 3 error - function value not converging
- *-- 4 error - original values do not bracket
- *-- a root
- *-- 5 error - maximum iterations exceeded
- *-----------------------------------------------------------------------
-
- parameters cFunc, fNearx, fFarx, fAbserr, nMaxiter, ;
- n_Flag, xPass1, xPass2, xPass3
- private nSplits, fBracket, fFary, fNeary, nIters
- private fMaxabs, fOldx, fOldy, fDiffx, fAbsdiff, fSecant
-
- store 0 to m->nSplits, m->nIters
- m->fBracket = abs ( m->fNearX - m->fFarX )
- m->fFarY = &cFunc.( m->fFarX, m->xPass1, m->xPass2, m->xPass3 )
- m->fNearY = &cFunc.( m->fNearX, m->xPass1, m->xPass2, m->xPass3 )
-
- if sign( m->fNearY ) = sign( m->fFarY )
- m->n_Flag = 4
- return float(0)
- endif
-
- m->fMaxAbs = max( abs( m->fNearY ), abs( m->fFarY ) )
- m->n_Flag = 0
-
- * Main iteration loop
-
- do while .t.
-
- if abs( m->fFarY ) < abs( m->fNearY )
-
- * Interchange fNearX and fFarx so that
- * fNearX is closer to a solution--
- * abs( fNearY ) <= abs( fFary )
-
- m->fOldX = m->fNearX
- m->fOldY = m->fNearY
- m->fNearX = m->fFarX
- m->fNearY = m->fFarY
- m->fFarX = m->fOldX
- m->fFarY = m->fOldY
- endif
-
- m->fDiffX = m->fFarX - m->fNearX
- m->fAbsDiff = abs( m->fDiffX )
-
- * Test whether interval is too small to continue
-
- if m->fAbsDiff <= 2 * m->fAbsErr
- if abs( m->fNearY ) > m->fMaxAbs
-
- * Yes, but we are out of bounds
-
- m->n_Flag = 3
- m->fNearX = float(0)
- else
-
- * Yes, and we have a solution!
-
- m->n_Flag = 1
- endif
- exit
- endif
-
- * Save the last approximation to x and y
-
- m->fOldX = m->fNearX
- m->fOldY = m->fNearY
-
- * Check if reduction in the size of
- * bracketing interval is satisfactory.
- * If not, bisect until it is.
-
- m->nSplits = m->nSplits + 1
- if m->nSplits >= 4
- if 4 * m->fAbsDiff >= m->fBracket
- m->fNearX = m->fNearX + m->fDiffX / 2
- else
- m->nSplits = 0
- m->fBracket = m->fAbsDiff / 2
-
- * Calculate secant
-
- m->fSecant = ( m->fNearX - m->fFarX ) * m->fNearY ;
- / ( m->fFarY - m->fNearY )
-
- * But not less than error allowed
-
- if abs( m->fSecant ) < m->fAbsErr
- m->fNearX = m->fNearX + m->fAbsErr * sign( m->fDiffX )
- else
- m->fNearX = m->fNearX + m->fSecant
- endif
- endif
- endif
-
- * Evaluate the function at the new approximation
-
- m->fNearY = &cFunc.( m->fNearX, m->xPass1, m->xPass2, m->xPass3 )
-
- * If it's exactly zero, we win! Run with it
-
- if m->fNearY = 0.00
- m->n_Flag = 2
- exit
- endif
-
- * Else adjust iteration count and quit if too
- * many iterations with no solution
-
- m->nIters = m->nIters + 1
- if m->nIters > nMaxiter
- m->n_Flag = 5
- m->fNearX = float( 0 )
- exit
- endif
-
- * And finally keep as the new fFarx that one
- * of the previous approximations, fFarx and
- * fOldx, at which the function has a sign opposite
- * to that at the new approximation, fNearx.
-
- if sign( m->fNearY ) = sign( m->fFarY )
- m->fFarX = m->fOldX
- m->fFarY = m->fOldY
- endif
- enddo
-
- RETURN m->fNearX
- *-- EoF: Zeroin()
-
- FUNCTION ArrayRows
- *-----------------------------------------------------------------------
- *-- Programmer..: Jay Parsons (CIS: 72662,1302)
- *-- Date........: 03/01/1992
- *-- Notes.......: Number of Rows in an array
- *-- Written for.: dBASE IV, 1.1
- *-- Rev. History: 03/01/1992 -- Original Release
- *-- Calls.......: None
- *-- Called by...: Any
- *-- Usage.......: ArrayRows("<aArray>")
- *-- Example.....: n = ArrayRows("aTest")
- *-- Returns.....: numeric
- *-- Parameters..: aArray = Name of array
- *-----------------------------------------------------------------------
-
- parameters aArray
- private nHi, nLo, nTrial, nDims
- m->nLo = 1
- m->nHi = 1170
- if type( "&aArray.[ 1, 1 ]" ) = "U"
- m->nDims = 1
- else
- m->nDims = 2
- endif
- do while .T.
- m->nTrial = int( ( m->nHi + m->nLo ) / 2 )
- if m->nHi < m->nLo
- exit
- endif
- if m->nDims = 1 .and. type( "&aArray.[ m->nTrial ]" ) = "U" ;
- .or. m->nDims = 2 .and. type("&aArray.[ m->nTrial, 1 ]");
- = "U"
- m->nHi = m->nTrial - 1
- else
- m->nLo = m->nTrial + 1
- endif
- enddo
-
- RETURN m->nTrial
- *-- EoF: ArrayRows()
-
- *-----------------------------------------------------------------------
- *-- EoP: FINANCE.PRG
- *-----------------------------------------------------------------------